<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#FFF"><meta name="baidu-site-verification" content="code-DvHFUp3jdk"><meta name="baidu-site-verification" content="code-eHnRZZHAeP"><meta name="baidu-site-verification" content="code-EmlZBboqEW"><meta name="baidu-site-verification" content="code-Hf9ZXEHZ41"><meta name="360-site-verification" content="941073b91e9afaf02748ba000ba47e80"><meta name="360-site-verification" content="010bd45f0d523ce46f298dd597ef0289"><meta name="sogou_site_verification" content="hlLPNfmIjv"><meta name="sogou_site_verification" content="y3La2dTB20"><meta name="msvalidate.01" content="03829B28CC46A7AA4B5503897F475BA1"><script>!function(){var s="https://s.ssl.qhres2.com/ssl/ab77b6ea7f3fbf79.js";document.write('<script src="'+s+'" id="sozz"><\/script>')}()</script><script>!function(t,e,c,n,r,a,s){t[c]=t[c]||function(){(t[c].q=t[c].q||[]).push(arguments)},a=e.createElement(n),a.async=1,a.src="https://www.clarity.ms/tag/"+r+"?ref=bwt",s=e.getElementsByTagName(n)[0],s.parentNode.insertBefore(a,s)}(window,document,"clarity","script","8xwpfccwj2")</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?1d318b0509ca0909017253ed6f659ae4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?aea6c1471d6299bdd1c87a0836f18675";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?498ca413655655a62058d6b5d7c62786";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?e19e8161e13f718806b79f49240583b4";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png"><link rel="icon" type="image/ico" sizes="32x32" href="/images/favicon.ico"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="UA-139249935-1"><meta name="baidu-site-verification" content="cOtBu9CtTEIuwJeN"><link rel="alternate" type="application/rss+xml" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/rss.xml"><link rel="alternate" type="application/atom+xml" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/atom.xml"><link rel="alternate" type="application/json" title="了尘兰若的小坑" href="https://liaochenlanruo.gitee.io/feed.json"><link rel="stylesheet" href="//fonts.googleapis.com/css?family=Mulish:300,300italic,400,400italic,700,700italic%7CFredericka%20the%20Great:300,300italic,400,400italic,700,700italic%7CNoto%20Serif%20JP:300,300italic,400,400italic,700,700italic%7CNoto%20Serif%20SC:300,300italic,400,400italic,700,700italic%7CInconsolata:300,300italic,400,400italic,700,700italic&display=swap&subset=latin,latin-ext"><link rel="stylesheet" href="/css/app.css?v=0.2.5"><meta name="keywords" content="编程,Shiny入门系列"><link rel="canonical" href="https://liaochenlanruo.gitee.io/post/e6be.html"><title>Shiny从入门到入定——10-动态UI - IT | ResearchGo = 了尘兰若的小坑 = liaochenlanruo</title><meta name="generator" content="Hexo 5.4.0"></head><body itemscope itemtype="http://schema.org/WebPage"><div id="loading"><div class="cat"><div class="body"></div><div class="head"><div class="face"></div></div><div class="foot"><div class="tummy-end"></div><div class="bottom"></div><div class="legs left"></div><div class="legs right"></div></div><div class="paw"><div class="hands left"></div><div class="hands right"></div></div></div></div><div id="container"><header id="header" itemscope itemtype="http://schema.org/WPHeader"><div class="inner"><div id="brand"><div class="pjax"><h1 itemprop="name headline">Shiny从入门到入定——10-动态UI</h1><div class="meta"><span class="item" title="Created: 2024-04-27 17:05:46"><span class="icon"><i class="ic i-calendar"></i> </span><span class="text">Posted on</span> <time itemprop="dateCreated datePublished" datetime="2024-04-27T17:05:46+08:00">2024-04-27</time> </span><span class="item" title="Symbols count in article"><span class="icon"><i class="ic i-pen"></i> </span><span class="text">Symbols count in article</span> <span>20k</span> <span class="text">words</span> </span><span class="item" title="Reading time"><span class="icon"><i class="ic i-clock"></i> </span><span class="text">Reading time</span> <span>19 mins.</span></span></div></div></div><nav id="nav"><div class="inner"><div class="toggle"><div class="lines" aria-label="Toggle navigation bar"><span class="line"></span> <span class="line"></span> <span class="line"></span></div></div><ul class="menu"><li class="item title"><a href="/" rel="start">ResearchGo</a></li></ul><ul class="right"><li class="item theme"><i class="ic i-sun"></i></li><li class="item search"><i class="ic i-search"></i></li></ul></div></nav></div><div id="imgs" class="pjax"><img src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/custom/bgs/thumb_210.webp"></div></header><div id="waves"><svg class="waves" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 24 150 28" preserveAspectRatio="none" shape-rendering="auto"><defs><path id="gentle-wave" d="M-160 44c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v44h-352z"/></defs><g class="parallax"><use xlink:href="#gentle-wave" x="48" y="0"/><use xlink:href="#gentle-wave" x="48" y="3"/><use xlink:href="#gentle-wave" x="48" y="5"/><use xlink:href="#gentle-wave" x="48" y="7"/></g></svg></div><main><div class="inner"><div id="main" class="pjax"><div class="article wrap"><div class="breadcrumb" itemscope itemtype="https://schema.org/BreadcrumbList"><i class="ic i-home"></i> <span><a href="/">Home</a></span><i class="ic i-angle-right"></i> <span class="current" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"><a href="/categories/IT/" itemprop="item" rel="index" title="In IT"><span itemprop="name">IT</span></a><meta itemprop="position" content="1"></span></div><article itemscope itemtype="http://schema.org/Article" class="post block" lang="en"><link itemprop="mainEntityOfPage" href="https://liaochenlanruo.gitee.io/post/e6be.html"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/images/head.jpg"><meta itemprop="name" content="Hualin Liu"><meta itemprop="description" content="liaochenlanruo, 分享微生物生物信息学分析方法，欢迎加入QQ群交流945751012，不接受群内广告！"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="了尘兰若的小坑"></span><div class="body md" itemprop="articleBody"><div class="gallery" itemscope itemtype="http://schema.org/ImageGallery"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/custom/bgs/thumb_210.webp" itemprop="contentUrl"></div><h1 id="10-动态ui"><a class="anchor" href="#10-动态ui">#</a> 10 动态 UI</h1><p>到目前为止，我们已经看到了 UI 和 server 函数之间的清晰分离：用户界面在应用程序启动时静态定义，因此它无法对应用程序中发生的任何事情做出响应。在本章中，您将学习如何创建动态用户界面，通过 server 函数中运行的代码来更改 UI。</p><p>创建动态用户界面有三个关键技术：</p><ul><li>使用 <code>update</code> 系列的函数来修改输入控件的参数。</li><li>使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdGFic2V0UGFuZWwuaHRtbA=="> tabsetPanel ()</span> 来有条件地显示和隐藏用户界面的部分。</li><li>使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vaHRtbE91dHB1dC5odG1s"> uiOutput ()</span> 和<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vcmVuZGVyVUkuaHRtbA=="> renderUI ()</span> 通过代码生成用户界面的选定部分。</li></ul><p>这三个工具为您提供了相当强大的功能，通过修改输入和输出来响应用户。我将演示一些您可以应用它们的更有用的方式，但最终您的创造力是唯一的限制。同时，这些工具可能会使您的应用程序变得更难理解，因此请谨慎使用，并始终努力使用解决您问题的最简单技术。</p><p>接下来是 R 代码库的加载部分：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>library<span class="token punctuation">(</span>shiny<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>library<span class="token punctuation">(</span>dplyr<span class="token punctuation">,</span> warn.conflicts <span class="token operator">=</span> <span class="token boolean">FALSE</span><span class="token punctuation">)</span></pre></td></tr></table></figure><h2 id="101-更新输入"><a class="anchor" href="#101-更新输入">#</a> 10.1 更新输入</h2><p>我们将从一个简单的技术开始，该技术允许您在创建后修改输入： <code>update</code> 系列的函数。每个输入控件，例如 <code>textInput()</code> ，都配有一个更新函数，例如 <code>updateTextInput()</code> ，允许您在创建后修改该控件。</p><p>请考虑以下代码示例，结果如 <code>图10.1</code> 所示。该应用程序有两个输入控件，它们控制另一个输入控件（滑块）的范围（最小值和最大值）。关键的想法是使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vb2JzZXJ2ZUV2ZW50Lmh0bWw="> observeEvent ()</span> 来触发<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlU2xpZGVySW5wdXQuaHRtbA=="> updateSliderInput ()</span>，每当最小或最大输入发生变化时。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"min"</span><span class="token punctuation">,</span> <span class="token string">"Minimum"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"max"</span><span class="token punctuation">,</span> <span class="token string">"Maximum"</span><span class="token punctuation">,</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  sliderInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"n"</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>min<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    updateSliderInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"n"</span><span class="token punctuation">,</span> min <span class="token operator">=</span> input<span class="token operator">$</span>min<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span>  </pre></td></tr><tr><td data-num="10"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>max<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    updateSliderInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"n"</span><span class="token punctuation">,</span> max <span class="token operator">=</span> input<span class="token operator">$</span>max<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>在这个例子中， <code>observeEvent()</code> 监视 <code>min</code> 和 <code>max</code> 输入的变化，并在它们变化时调用 <code>updateSliderInput()</code> 来更新滑块的最小和最大值。 <code>renderText()</code> 用于在 <code>textOutput()</code> 控件中显示当前范围。</p><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/45d9b7c2dabd64a4597e370d2b84c0dc01cf8a2f/2dc99/demos/action-dynamic/update-basics-onload.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/e5a1387f21bee28260731f6fb84991e188af5ec2/8cfbf/demos/action-dynamic/update-basics-max-increase.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/fcca996a780ec2cbaf306e764a577649e6fdca4b/5ecfc/demos/action-dynamic/update-basics-min-decrease.png"></td></tr><tr><td colspan="3">图10.1 应用程序加载时的界面（左），增加最大值后的界面（中），然后减少最小值后的界面（右）。请访问 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwZGF0ZS1iYXNpY3M=">https://hadley.shinyapps.io/ms-update-basics</span> 查看实时效果</td></tr></table><p>更新函数与其他 Shiny 函数有些不同：它们都接受输入的名称（作为字符串）作为 <code>inputId</code> 参数。其余的参数对应于输入构造函数中可以在创建后修改的参数。</p><p>为了帮助您掌握更新函数的使用，我将展示几个简单的示例，然后我们将深入探讨使用分层选择框的复杂案例研究，最后讨论循环引用的问题。</p><h3 id="1011-简单应用"><a class="anchor" href="#1011-简单应用">#</a> 10.1.1 简单应用</h3><p>更新函数最简单的用法是为用户提供一些小的便利。例如，您可能希望轻松地将参数重置为其初始值。以下代码片段展示了如何结合使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vYWN0aW9uQnV0dG9uLmh0bWw="> actionButton ()</span>、<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vb2JzZXJ2ZUV2ZW50Lmh0bWw=">observeEvent()</span> 和<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlU2xpZGVySW5wdXQuaHRtbA=="> updateSliderInput ()</span>，结果如 <code>图10.2</code> 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sliderInput<span class="token punctuation">(</span><span class="token string">"x1"</span><span class="token punctuation">,</span> <span class="token string">"x1"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">10</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  sliderInput<span class="token punctuation">(</span><span class="token string">"x2"</span><span class="token punctuation">,</span> <span class="token string">"x2"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">10</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  sliderInput<span class="token punctuation">(</span><span class="token string">"x3"</span><span class="token punctuation">,</span> <span class="token string">"x3"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token operator">-</span><span class="token number">10</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  actionButton<span class="token punctuation">(</span><span class="token string">"reset"</span><span class="token punctuation">,</span> <span class="token string">"Reset"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre></pre></td></tr><tr><td data-num="8"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>reset<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    updateSliderInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"x1"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    updateSliderInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"x2"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    updateSliderInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"x3"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/b319bdb38cce5a357ba4131d17fec8b3a8bd34fd/cc473/demos/action-dynamic/update-reset-onload.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/b5061a18295fbec4f08dd399e32ce252d2ad6eb9/230d4/demos/action-dynamic/update-reset-set.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/b319bdb38cce5a357ba4131d17fec8b3a8bd34fd/8c5f7/demos/action-dynamic/update-reset-reset.png"></td></tr><tr><td colspan="3">图10.2 应用加载时（左图），拖动一些滑块后（中图），然后点击重置（右图）。在线查看地址：<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwZGF0ZS1yZXNldA==">https://hadley.shinyapps.io/ms-update-reset</span></td></tr></table><p>一个类似的应用是调整动作按钮的文本，以便你确切知道它将执行什么操作。图 <code>10.3</code> 展示了下面代码的结果。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Simulations"</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  actionButton<span class="token punctuation">(</span><span class="token string">"simulate"</span><span class="token punctuation">,</span> <span class="token string">"Simulate"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    label <span class="token operator">&lt;-</span> paste0<span class="token punctuation">(</span><span class="token string">"Simulate "</span><span class="token punctuation">,</span> input<span class="token operator">$</span>n<span class="token punctuation">,</span> <span class="token string">" times"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    updateActionButton<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"simulate"</span><span class="token punctuation">,</span> label <span class="token operator">=</span> label<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/f6604343f39fe08be631b06ddac37b2c00d73ea5/f636b/demos/action-dynamic/update-button-onload.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/d65b9991e6b08e57700bf2cc8f5fafafe59ebc4e/03e2a/demos/action-dynamic/update-button-set1.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/a2a75084db3d975c48dc56ef70eebd2906eae356/a924e/demos/action-dynamic/update-button-set100.png"></td></tr><tr><td colspan="3">图 10.3 应用加载时（左），将模拟次数设置为 1（中），再将模拟次数设置为 100（右）。在线查看地址：<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwZGF0ZS1idXR0b24=">https://hadley.shinyapps.io/ms-update-button</span></td></tr></table><p>有很多方法可以用这种方式使用更新函数；在开发复杂应用时，要注意找出向用户提供更多信息的方法。一个特别重要的应用是通过逐步筛选来简化从一长串可能选项中进行选择的过程。这通常是 “分层选择框” 的问题。</p><h3 id="1012-分层选择框"><a class="anchor" href="#1012-分层选择框">#</a> 10.1.2 分层选择框</h3><p>更新函数的一个更复杂但特别有用的应用是允许在多个类别之间进行交互式深入探索。我将用一个来自 <span class="exturl" data-url="aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9reWFueW9nYS9zYW1wbGUtc2FsZXMtZGF0YQ==">https://www.kaggle.com/kyanyoga/sample-sales-data</span> 的销售仪表板的虚拟数据来说明它们的使用方法。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>sales <span class="token operator">&lt;-</span> vroom<span class="token operator">::</span>vroom<span class="token punctuation">(</span><span class="token string">"sales-dashboard/sales_data_sample.csv"</span><span class="token punctuation">,</span> col_types <span class="token operator">=</span> list<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> na <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>sales <span class="token percent-operator operator">%>%</span> </pre></td></tr><tr><td data-num="3"></td><td><pre>  select<span class="token punctuation">(</span>TERRITORY<span class="token punctuation">,</span> CUSTOMERNAME<span class="token punctuation">,</span> ORDERNUMBER<span class="token punctuation">,</span> everything<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token percent-operator operator">%>%</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  arrange<span class="token punctuation">(</span>ORDERNUMBER<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token comment">#> # A tibble: 2,823 × 25</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token comment">#>    TERRITORY CUSTOM…¹ ORDER…² QUANT…³ PRICE…⁴ ORDER…⁵ SALES ORDER…⁶ STATUS QTR_ID</span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token comment">#>    &lt;chr>     &lt;chr>      &lt;dbl>   &lt;dbl>   &lt;dbl>   &lt;dbl> &lt;dbl> &lt;chr>   &lt;chr>   &lt;dbl></span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token comment">#>  1 NA        Online …   10100      30   100         3 5151  1/6/20… Shipp…      1</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token comment">#>  2 NA        Online …   10100      50    67.8       2 3390  1/6/20… Shipp…      1</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token comment">#>  3 NA        Online …   10100      22    86.5       4 1903. 1/6/20… Shipp…      1</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token comment">#>  4 NA        Online …   10100      49    34.5       1 1689. 1/6/20… Shipp…      1</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token comment">#>  5 EMEA      Blauer …   10101      25   100         4 3782  1/9/20… Shipp…      1</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token comment">#>  6 EMEA      Blauer …   10101      26   100         1 3773. 1/9/20… Shipp…      1</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token comment">#>  7 EMEA      Blauer …   10101      45    31.2       3 1404  1/9/20… Shipp…      1</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token comment">#>  8 EMEA      Blauer …   10101      46    53.8       2 2473. 1/9/20… Shipp…      1</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token comment">#>  9 NA        Vitachr…   10102      39   100         2 4808. 1/10/2… Shipp…      1</span></pre></td></tr><tr><td data-num="17"></td><td><pre><span class="token comment">#> 10 NA        Vitachr…   10102      41    50.1       1 2056. 1/10/2… Shipp…      1</span></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token comment">#> # … with 2,813 more rows, 15 more variables: MONTH_ID &lt;dbl>, YEAR_ID &lt;dbl>,</span></pre></td></tr><tr><td data-num="19"></td><td><pre><span class="token comment">#> #   PRODUCTLINE &lt;chr>, MSRP &lt;dbl>, PRODUCTCODE &lt;chr>, PHONE &lt;chr>,</span></pre></td></tr><tr><td data-num="20"></td><td><pre><span class="token comment">#> #   ADDRESSLINE1 &lt;chr>, ADDRESSLINE2 &lt;chr>, CITY &lt;chr>, STATE &lt;chr>,</span></pre></td></tr><tr><td data-num="21"></td><td><pre><span class="token comment">#> #   POSTALCODE &lt;chr>, COUNTRY &lt;chr>, CONTACTLASTNAME &lt;chr>,</span></pre></td></tr><tr><td data-num="22"></td><td><pre><span class="token comment">#> #   CONTACTFIRSTNAME &lt;chr>, DEALSIZE &lt;chr>, and abbreviated variable names</span></pre></td></tr><tr><td data-num="23"></td><td><pre><span class="token comment">#> #   ¹​CUSTOMERNAME, ²​ORDERNUMBER, ³​QUANTITYORDERED, ⁴​PRICEEACH, ⁵​ORDERLINENUMBER,</span></pre></td></tr><tr><td data-num="24"></td><td><pre><span class="token comment">#> #   ⁶​ORDERDATE</span></pre></td></tr></table></figure><p>在这个演示中，我将重点关注数据中的自然层次结构：</p><ul><li><p>每个区域包含客户。</p></li><li><p>每个客户有多个订单。</p></li><li><p>每个订单包含行。</p></li></ul><p>我想创建一个用户界面，你可以：</p><ul><li><p>选择一个区域来查看所有客户。</p></li><li><p>选择一个客户来查看所有订单。</p></li><li><p>选择一个订单来查看底层行。</p></li></ul><p>用户界面的核心是简单的：我将创建三个选择框和一个输出表格。 <code>customername</code> 和 <code>ordernumber</code> 选择框的选项将动态生成，因此我将设置 <code>choices = NULL</code> 。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"territory"</span><span class="token punctuation">,</span> <span class="token string">"Territory"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> unique<span class="token punctuation">(</span>sales<span class="token operator">$</span>TERRITORY<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"customername"</span><span class="token punctuation">,</span> <span class="token string">"Customer"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"ordernumber"</span><span class="token punctuation">,</span> <span class="token string">"Order number"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  tableOutput<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>在服务器函数中，我自上而下地工作：</p><ol><li><p>我创建了一个响应式对象 <code>territory()</code> ，它包含与所选区域匹配的 <code>sales</code> 中的行。</p></li><li><p>每当 <code>territory()</code> 发生变化时，我都会更新 <code>input$customername</code> 选择框中的选项列表。</p></li><li><p>我创建了另一个响应式对象 <code>customer()</code> ，它包含与所选客户匹配的 <code>territory()</code> 中的行。</p></li><li><p>每当 <code>customer()</code> 发生变化时，我都会更新 <code>input$ordernumber</code> 选择框中的选项列表。</p></li><li><p>我在 <code>output$data</code> 中显示所选订单。</p></li></ol><p>你可以看到下面的组织结构：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  territory <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    filter<span class="token punctuation">(</span>sales<span class="token punctuation">,</span> TERRITORY <span class="token operator">==</span> input<span class="token operator">$</span>territory<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  observeEvent<span class="token punctuation">(</span>territory<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    choices <span class="token operator">&lt;-</span> unique<span class="token punctuation">(</span>territory<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">$</span>CUSTOMERNAME<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    updateSelectInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"customername"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> choices<span class="token punctuation">)</span> </pre></td></tr><tr><td data-num="8"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  </pre></td></tr><tr><td data-num="10"></td><td><pre>  customer <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    req<span class="token punctuation">(</span>input<span class="token operator">$</span>customername<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    filter<span class="token punctuation">(</span>territory<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> CUSTOMERNAME <span class="token operator">==</span> input<span class="token operator">$</span>customername<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>  observeEvent<span class="token punctuation">(</span>customer<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    choices <span class="token operator">&lt;-</span> unique<span class="token punctuation">(</span>customer<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">$</span>ORDERNUMBER<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    updateSelectInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"ordernumber"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> choices<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  </pre></td></tr><tr><td data-num="19"></td><td><pre>  output<span class="token operator">$</span>data <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>    req<span class="token punctuation">(</span>input<span class="token operator">$</span>ordernumber<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    customer<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token percent-operator operator">%>%</span> </pre></td></tr><tr><td data-num="22"></td><td><pre>      filter<span class="token punctuation">(</span>ORDERNUMBER <span class="token operator">==</span> input<span class="token operator">$</span>ordernumber<span class="token punctuation">)</span> <span class="token percent-operator operator">%>%</span> </pre></td></tr><tr><td data-num="23"></td><td><pre>      select<span class="token punctuation">(</span>QUANTITYORDERED<span class="token punctuation">,</span> PRICEEACH<span class="token punctuation">,</span> PRODUCTCODE<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="25"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/fe6c81ed4946e87b044fc7cc91b3bb05e4954043/7d64e/demos/action-dynamic/update-nested-territory.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/d3a0d284501015f737373cc8e6f14b27a49de373/0e06a/demos/action-dynamic/update-nested-customername.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/b0ae629ca1909719117abfce2d890db7bf379412/37026/demos/action-dynamic/update-nested-orders.png"></td></tr><tr><td colspan="3">图 10.4 我选择“EMEA”（左），然后选择“Lyon Souveniers”（中），然后（右）查看订单。在线查看地址：<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwZGF0ZS1uZXN0ZWQ=">https://hadley.shinyapps.io/ms-update-nested</span></td></tr></table><p>你可以在 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXVwZGF0ZS1uZXN0ZWQ=">https://hadley.shinyapps.io/ms-update-nested</span> 尝试这个简单的示例，或者在 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2hhZGxleS9tYXN0ZXJpbmctc2hpbnkvdHJlZS9tYXN0ZXIvc2FsZXMtZGFzaGJvYXJk">https://github.com/hadley/mastering-shiny/tree/master/sales-dashboard</span> 查看一个更加完善的应用示例。</p><h3 id="1013-冻结响应式输入"><a class="anchor" href="#1013-冻结响应式输入">#</a> 10.1.3 冻结响应式输入</h3><p>有时，这种分层选择会短暂地创建一个无效的输入集，导致出现不想要的输出闪烁。例如，考虑这个简单的应用，你首先选择一个数据集，然后选择要汇总的变量：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"dataset"</span><span class="token punctuation">,</span> <span class="token string">"Choose a dataset"</span><span class="token punctuation">,</span> c<span class="token punctuation">(</span><span class="token string">"pressure"</span><span class="token punctuation">,</span> <span class="token string">"cars"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"column"</span><span class="token punctuation">,</span> <span class="token string">"Choose column"</span><span class="token punctuation">,</span> character<span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  verbatimTextOutput<span class="token punctuation">(</span><span class="token string">"summary"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre></pre></td></tr><tr><td data-num="7"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  dataset <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span>get<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">"package:datasets"</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  </pre></td></tr><tr><td data-num="10"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    updateSelectInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"column"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> names<span class="token punctuation">(</span>dataset<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  </pre></td></tr><tr><td data-num="14"></td><td><pre>  output<span class="token operator">$</span>summary <span class="token operator">&lt;-</span> renderPrint<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    summary<span class="token punctuation">(</span>dataset<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">[</span>input<span class="token operator">$</span>column<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>如果你在 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLWZyZWV6ZQ==">https://hadley.shinyapps.io/ms-freeze</span> 上尝试这个实时应用，你会注意到当你切换数据集时，摘要输出会短暂地闪烁。这是因为 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlU2VsZWN0SW5wdXQuaHRtbA==">updateSelectInput()</span> 只有在所有输出和观察者都运行之后才会生效，因此会暂时出现一个状态，即你拥有数据集 B 和来自数据集 A 的变量，因此输出会包含 <code>summary(NULL)</code> 。</p><p>你可以通过 “freezing” 输入值来解决这个问题，使用 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZnJlZXplUmVhY3RpdmVWYWx1ZS5odG1s">freezeReactiveValue()</span>。这确保了任何使用输入的响应式对象或输出都不会更新，直到下一次完整的失效周期。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  dataset <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span>get<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">"package:datasets"</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  </pre></td></tr><tr><td data-num="4"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    freezeReactiveValue<span class="token punctuation">(</span>input<span class="token punctuation">,</span> <span class="token string">"column"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    updateSelectInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"column"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> names<span class="token punctuation">(</span>dataset<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  </pre></td></tr><tr><td data-num="9"></td><td><pre>  output<span class="token operator">$</span>summary <span class="token operator">&lt;-</span> renderPrint<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    summary<span class="token punctuation">(</span>dataset<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">[</span>input<span class="token operator">$</span>column<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>请注意，你不需要 “thaw” 输入值；当 Shiny 检测到会话和服务器再次同步时，这会自动发生。</p><p>你可能会好奇什么时候应该使用 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vZnJlZXplUmVhY3RpdmVWYWx1ZS5odG1s">freezeReactiveValue()</span>：实际上，当你动态更改输入值时，使用它总是一个好习惯。实际的修改需要一些时间才能流向浏览器，然后再返回给 Shiny，而在此期间，任何对该值的读取都可能是浪费的，在最坏的情况下可能导致错误。使用 <code>freezeReactiveValue()</code> 告诉所有下游计算，输入值是陈旧的，它们应该保存它们的努力，直到它变得有用。</p><h3 id="1014-循环引用"><a class="anchor" href="#1014-循环引用">#</a> 10.1.4 循环引用</h3><p>如果你想使用 update 函数来改变输入的当前 <code>value</code> ，那么我们需要讨论一个重要的问题。从 Shiny 的角度来看，使用 update 函数来修改值与用户通过点击或输入来修改值没有区别。这意味着 update 函数可以像人类一样触发响应式更新。这意味着你现在已经超出了纯响应式编程的范围，你需要开始担心循环引用和无限循环的问题。</p><p>例如，看看下面这个简单的应用。它包含一个输入控件和一个观察者，后者将其值加一并更新。每次运行 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlTnVtZXJpY0lucHV0Lmh0bWw=">updateNumericInput()</span> 时，它都会更改 <code>input$n</code> ，导致 <code>updateNumericInput()</code> 再次运行，因此应用陷入无限循环，持续增加 <code>input$n</code> 的值。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    updateNumericInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"n"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> input<span class="token operator">$</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>虽然你不太可能在自己的应用中创建这种显而易见的问题，但如果你在更新相互依赖的多个控件时，可能会遇到类似的问题，如下一个例子所示。</p><h3 id="1015-相互关联的输入"><a class="anchor" href="#1015-相互关联的输入">#</a> 10.1.5 相互关联的输入</h3><p>在应用中出现循环引用很容易发生在有多个 “事实来源” 时。例如，假设你想创建一个温度转换应用，用户既可以输入摄氏温度也可以输入华氏温度：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"temp_c"</span><span class="token punctuation">,</span> <span class="token string">"Celsius"</span><span class="token punctuation">,</span> <span class="token keyword">NA</span><span class="token punctuation">,</span> step <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"temp_f"</span><span class="token punctuation">,</span> <span class="token string">"Fahrenheit"</span><span class="token punctuation">,</span> <span class="token keyword">NA</span><span class="token punctuation">,</span> step <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre></pre></td></tr><tr><td data-num="6"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>temp_f<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    c <span class="token operator">&lt;-</span> round<span class="token punctuation">(</span><span class="token punctuation">(</span>input<span class="token operator">$</span>temp_f <span class="token operator">-</span> <span class="token number">32</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">5</span> <span class="token operator">/</span> <span class="token number">9</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    updateNumericInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"temp_c"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> c<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  </pre></td></tr><tr><td data-num="12"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>temp_c<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    f <span class="token operator">&lt;-</span> round<span class="token punctuation">(</span><span class="token punctuation">(</span>input<span class="token operator">$</span>temp_c <span class="token operator">*</span> <span class="token number">9</span> <span class="token operator">/</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">32</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    updateNumericInput<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"temp_f"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> f<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>如果你尝试这个应用，<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXRlbXBlcmF0dXJl">https://hadley.shinyapps.io/ms-temperature</span>，你会发现它大部分时候可以正常工作，但你可能也会注意到它有时会触发多次更改。例如：</p><ul><li><p>将温度设为 120 华氏度，然后点击向下的箭头。</p></li><li><p>华氏度变为 119，摄氏度更新为 48。</p></li><li><p>48 摄氏度转换为 118 华氏度，因此华氏度再次变为 118。</p></li><li><p>幸运的是，118 华氏度仍然是 48 摄氏度，所以更新在那里停止了。</p></li></ul><p>这个问题没有解决办法，因为你在应用中有一个概念（温度），但有两个表达式（摄氏度和华氏度）。在这里我们很幸运，因为循环迅速收敛到一个同时满足两个约束的值。一般来说，除非你愿意非常仔细地分析你创建的底层动态系统的收敛性质，否则最好避免这种情况。</p><h3 id="1016-练习"><a class="anchor" href="#1016-练习">#</a> 10.1.6 练习</h3><ol><li><p>请在下面的用户界面中添加一个服务器函数，以更新 <code>input$date</code> ，这样你只能选择 <code>input$year</code> 中的日期。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"year"</span><span class="token punctuation">,</span> <span class="token string">"year"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">2020</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  dateInput<span class="token punctuation">(</span><span class="token string">"date"</span><span class="token punctuation">,</span> <span class="token string">"date"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>请在下面的用户界面中添加一个服务器函数，根据 <code>input$state</code> 更新 <code>input$county</code> 的选择。作为一个额外的挑战，请也将路易斯安那州的标签从 “County” 改为 “Parish”，阿拉斯加州的标签改为 “Borough”。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>library<span class="token punctuation">(</span>openintro<span class="token punctuation">,</span> warn.conflicts <span class="token operator">=</span> <span class="token boolean">FALSE</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre><span class="token comment">#> Loading required package: airports</span></pre></td></tr><tr><td data-num="3"></td><td><pre><span class="token comment">#> Loading required package: cherryblossom</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token comment">#> Loading required package: usdata</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token comment">#> Registered S3 methods overwritten by 'readr':</span></pre></td></tr><tr><td data-num="6"></td><td><pre><span class="token comment">#>   method                    from </span></pre></td></tr><tr><td data-num="7"></td><td><pre><span class="token comment">#>   as.data.frame.spec_tbl_df vroom</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token comment">#>   as_tibble.spec_tbl_df     vroom</span></pre></td></tr><tr><td data-num="9"></td><td><pre><span class="token comment">#>   format.col_spec           vroom</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token comment">#>   print.col_spec            vroom</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token comment">#>   print.collector           vroom</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token comment">#>   print.date_names          vroom</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token comment">#>   print.locale              vroom</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token comment">#>   str.col_spec              vroom</span></pre></td></tr><tr><td data-num="15"></td><td><pre>states <span class="token operator">&lt;-</span> unique<span class="token punctuation">(</span>county<span class="token operator">$</span>state<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre></pre></td></tr><tr><td data-num="17"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"state"</span><span class="token punctuation">,</span> <span class="token string">"State"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> states<span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"county"</span><span class="token punctuation">,</span> <span class="token string">"County"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>使用服务器函数完善下面的用户界面，根据 <code>input$continent</code> 更新 <code>input$country</code> 的选择。使用 <code>output$data</code> 显示所有匹配的行。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>library<span class="token punctuation">(</span>gapminder<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre>continents <span class="token operator">&lt;-</span> unique<span class="token punctuation">(</span>gapminder<span class="token operator">$</span>continent<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre></pre></td></tr><tr><td data-num="4"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"continent"</span><span class="token punctuation">,</span> <span class="token string">"Continent"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> continents<span class="token punctuation">)</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="6"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"country"</span><span class="token punctuation">,</span> <span class="token string">"Country"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  tableOutput<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure></li><li><p>扩展之前的应用，以便您还可以选择选择所有大洲，从而查看所有国家。您需要将 <code>“(All)”</code> 添加到选择列表中，然后在过滤时特殊处理它。</p></li><li><p>在<span class="exturl" data-url="aHR0cHM6Ly9jb21tdW5pdHkucnN0dWRpby5jb20vdC8yOTMwNw=="> https://community.rstudio.com/t/29307</span>? 上描述的问题的核心是什么？</p></li></ol><h2 id="102-动态可见性"><a class="anchor" href="#102-动态可见性">#</a> 10.2 动态可见性</h2><p>复杂性的下一步是有选择地显示和隐藏用户界面中的部分。如果您了解一些 JavaScript 和 CSS，那么可以使用更复杂的方法，但有一种有用的技术不需要任何额外的知识：使用选项卡集（如在 6.3.1 节中介绍的）隐藏可选的用户界面。这是一种巧妙的技巧，允许您根据需要显示和隐藏用户界面，而无需从头开始重新生成它（您将在下一节中学到）。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>      selectInput<span class="token punctuation">(</span><span class="token string">"controller"</span><span class="token punctuation">,</span> <span class="token string">"Show"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> paste0<span class="token punctuation">(</span><span class="token string">"panel"</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token operator">:</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      tabsetPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="8"></td><td><pre>        id <span class="token operator">=</span> <span class="token string">"switcher"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>        type <span class="token operator">=</span> <span class="token string">"hidden"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="10"></td><td><pre>        tabPanelBody<span class="token punctuation">(</span><span class="token string">"panel1"</span><span class="token punctuation">,</span> <span class="token string">"Panel 1 content"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="11"></td><td><pre>        tabPanelBody<span class="token punctuation">(</span><span class="token string">"panel2"</span><span class="token punctuation">,</span> <span class="token string">"Panel 2 content"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="12"></td><td><pre>        tabPanelBody<span class="token punctuation">(</span><span class="token string">"panel3"</span><span class="token punctuation">,</span> <span class="token string">"Panel 3 content"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>      <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre></pre></td></tr><tr><td data-num="18"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>controller<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="20"></td><td><pre>    updateTabsetPanel<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"switcher"</span><span class="token punctuation">,</span> selected <span class="token operator">=</span> input<span class="token operator">$</span>controller<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/177d392d2358ae933be3c5ae9a78b2f076d8cce0/f34ae/demos/action-dynamic/dynamic-panels.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/a4ab38457f2b2194dfd2a5e1792f1d38fff0b6dc/90b0c/demos/action-dynamic/dynamic-panels-panel2.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/7dc90302d452a421cc68547265384f212c58db8f/0dc9c/demos/action-dynamic/dynamic-panels-panel3.png"></td></tr><tr><td colspan="3">图10.5 选择panel1（左），然后选择panel2（中），最后选择panel3（右）。查看实时效果请访问 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLWR5bmFtaWMtcGFuZWxz">https://hadley.shinyapps.io/ms-dynamic-panels</span></td></tr></table><p>这里主要有两个想法：</p><ul><li><p>使用带有隐藏选项卡的选项卡集面板。</p></li><li><p>使用<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdXBkYXRlVGFic2V0UGFuZWwuaHRtbA=="> updateTabsetPanel ()</span> 从服务器切换选项卡。</p></li></ul><p>这是一个简单的想法，但结合一点创意，它将赋予你相当大的能力。接下来的两节将举例说明如何在实践中使用它的两个小例子。</p><h3 id="1021-条件用户界面"><a class="anchor" href="#1021-条件用户界面">#</a> 10.2.1 条件用户界面</h3><p>想象一下，你想要一个应用，允许用户模拟正态分布、均匀分布和指数分布。每种分布都有不同的参数，因此我们需要某种方法来显示不同分布的不同控件。在这里，我将为每个分布的唯一用户界面放在它自己的<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdGFiUGFuZWwuaHRtbA=="> tabPanel ()</span> 中，然后将三个选项卡组织成一个<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdGFic2V0UGFuZWwuaHRtbA=="> tabsetPanel ()</span>。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>parameter_tabs <span class="token operator">&lt;-</span> tabsetPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  id <span class="token operator">=</span> <span class="token string">"params"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  type <span class="token operator">=</span> <span class="token string">"hidden"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  tabPanel<span class="token punctuation">(</span><span class="token string">"normal"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"mean"</span><span class="token punctuation">,</span> <span class="token string">"mean"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"sd"</span><span class="token punctuation">,</span> <span class="token string">"standard deviation"</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  tabPanel<span class="token punctuation">(</span><span class="token string">"uniform"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="9"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"min"</span><span class="token punctuation">,</span> <span class="token string">"min"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"max"</span><span class="token punctuation">,</span> <span class="token string">"max"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  tabPanel<span class="token punctuation">(</span><span class="token string">"exponential"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    numericInput<span class="token punctuation">(</span><span class="token string">"rate"</span><span class="token punctuation">,</span> <span class="token string">"rate"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="14"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>然后，我将把这部分嵌入到一个更完整的用户界面中，允许用户选择样本数量，并显示结果的直方图：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>      selectInput<span class="token punctuation">(</span><span class="token string">"dist"</span><span class="token punctuation">,</span> <span class="token string">"Distribution"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="5"></td><td><pre>        choices <span class="token operator">=</span> c<span class="token punctuation">(</span><span class="token string">"normal"</span><span class="token punctuation">,</span> <span class="token string">"uniform"</span><span class="token punctuation">,</span> <span class="token string">"exponential"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>      <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Number of samples"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="8"></td><td><pre>      parameter_tabs<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="11"></td><td><pre>      plotOutput<span class="token punctuation">(</span><span class="token string">"hist"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>请注意，我已经仔细地将 <code>input$dist</code> 中的 <code>choices</code> 与选项卡面板的名称相匹配。这使得编写下面的<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vb2JzZXJ2ZUV2ZW50Lmh0bWw="> observeEvent ()</span> 代码变得很容易，该代码会在分布变化时自动切换控件。该应用的其他部分使用了你已经熟悉的技术。最终结果如图 10.6 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>dist<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    updateTabsetPanel<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"params"</span><span class="token punctuation">,</span> selected <span class="token operator">=</span> input<span class="token operator">$</span>dist<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span> </pre></td></tr><tr><td data-num="5"></td><td><pre>  </pre></td></tr><tr><td data-num="6"></td><td><pre>  sample <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    switch<span class="token punctuation">(</span>input<span class="token operator">$</span>dist<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="8"></td><td><pre>      normal <span class="token operator">=</span> rnorm<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">,</span> input<span class="token operator">$</span>mean<span class="token punctuation">,</span> input<span class="token operator">$</span>sd<span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>      uniform <span class="token operator">=</span> runif<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">,</span> input<span class="token operator">$</span>min<span class="token punctuation">,</span> input<span class="token operator">$</span>max<span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="10"></td><td><pre>      exponential <span class="token operator">=</span> rexp<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">,</span> input<span class="token operator">$</span>rate<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  output<span class="token operator">$</span>hist <span class="token operator">&lt;-</span> renderPlot<span class="token punctuation">(</span>hist<span class="token punctuation">(</span>sample<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> res <span class="token operator">=</span> <span class="token number">96</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/cce8beeaa545088bdba647ce61f6cb67f1eee04e/48a70/demos/action-dynamic/dynamic-conditional-normal.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/d4e7bf3c6b1d72e2733aeaf06741ab542607e288/882f0/demos/action-dynamic/dynamic-conditional-uniform.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/a5699032e1e6b89fcd6d2c87bcf2d8b616b82e8c/c0cb8/demos/action-dynamic/dynamic-conditional-exponential.png"></td></tr><tr><td colspan="3">图10.6 正态分布（左）、均匀分布（中）和指数分布（右）的结果。查看实时效果请访问 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLWR5bmFtaWMtY29uZGl0aW9uYWw=">https://hadley.shinyapps.io/ms-dynamic-conditional</span></td></tr></table><p>请注意，（例如） <code>input$mean</code> 的值是否对用户可见是独立的。底层的 HTML 控件仍然存在；只是你看不到它。</p><h3 id="1022-向导界面"><a class="anchor" href="#1022-向导界面">#</a> 10.2.2 向导界面</h3><p>你还可以使用这个想法来创建一个 “wizard”，这是一种界面类型，通过将其分散到多个页面上，更容易收集大量信息。在这里，我们在每个 “page” 中嵌入动作按钮，使其易于向前和向后移动。结果如图 10.7 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  tabsetPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    id <span class="token operator">=</span> <span class="token string">"wizard"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    type <span class="token operator">=</span> <span class="token string">"hidden"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    tabPanel<span class="token punctuation">(</span><span class="token string">"page_1"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="6"></td><td><pre>      <span class="token string">"Welcome!"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      actionButton<span class="token punctuation">(</span><span class="token string">"page_12"</span><span class="token punctuation">,</span> <span class="token string">"next"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    tabPanel<span class="token punctuation">(</span><span class="token string">"page_2"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="10"></td><td><pre>      <span class="token string">"Only one page to go"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="11"></td><td><pre>      actionButton<span class="token punctuation">(</span><span class="token string">"page_21"</span><span class="token punctuation">,</span> <span class="token string">"prev"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="12"></td><td><pre>      actionButton<span class="token punctuation">(</span><span class="token string">"page_23"</span><span class="token punctuation">,</span> <span class="token string">"next"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    tabPanel<span class="token punctuation">(</span><span class="token string">"page_3"</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="15"></td><td><pre>      <span class="token string">"You're done!"</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="16"></td><td><pre>      actionButton<span class="token punctuation">(</span><span class="token string">"page_32"</span><span class="token punctuation">,</span> <span class="token string">"prev"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre></pre></td></tr><tr><td data-num="21"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="22"></td><td><pre>  switch_page <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="23"></td><td><pre>    updateTabsetPanel<span class="token punctuation">(</span>inputId <span class="token operator">=</span> <span class="token string">"wizard"</span><span class="token punctuation">,</span> selected <span class="token operator">=</span> paste0<span class="token punctuation">(</span><span class="token string">"page_"</span><span class="token punctuation">,</span> i<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="24"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>  </pre></td></tr><tr><td data-num="26"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>page_12<span class="token punctuation">,</span> switch_page<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>page_21<span class="token punctuation">,</span> switch_page<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>page_23<span class="token punctuation">,</span> switch_page<span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="29"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>page_32<span class="token punctuation">,</span> switch_page<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="30"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/701f300df256124c9aa4cb5d9a6cb6245c7f70ce/e783b/demos/action-dynamic/wizard-1.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/8b0644dfec78873a45545620af591b31323fa11c/c61f6/demos/action-dynamic/wizard-2.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/9089ca7b0a519d51ae51c4991b26234561a41e67/18197/demos/action-dynamic/wizard-3.png"></td></tr><tr><td colspan="3">图10.7 向导界面将复杂的用户界面分割成多个页面。在这里，我们通过一个非常简单的示例来演示这个想法，点击“下一步”以进入下一页。查看实时效果请访问 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXdpemFyZA==">https://hadley.shinyapps.io/ms-wizard</span></td></tr></table><p>请注意，使用 <code>switch_page()</code> 函数可以减少服务器代码中的重复量。我们将在第 18 章再次回到这个想法，然后在第 <code>19.4.2</code> 节中创建一个模块来自动化向导界面。</p><h3 id="1023-练习"><a class="anchor" href="#1023-练习">#</a> 10.2.3 练习</h3><ol><li><p>使用隐藏的选项卡集，仅当用户选中 “advanced” 复选框时才显示额外的控件。</p></li><li><p>创建一个应用，该应用绘制 <code>ggplot(diamonds, aes(carat))</code> ，但允许用户选择使用的 geom：<span class="exturl" data-url="aHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21faGlzdG9ncmFtLmh0bWw=">geom_histogram()</span>、<span class="exturl" data-url="aHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21faGlzdG9ncmFtLmh0bWw=">geom_freqpoly()</span> 或<span class="exturl" data-url="aHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dlb21fZGVuc2l0eS5odG1s"> geom_density ()</span>。使用隐藏的选项卡集，允许用户根据 geom 选择不同的参数： <code>geom_histogram()</code> 和 <code>geom_freqpoly()</code> 具有 binwidth 参数； <code>geom_density()</code> 具有 bw 参数。</p></li><li><p>修改你在前一个练习中创建的应用，允许用户选择是否显示每个 geom（即，而不是始终使用一个 geom，他们可以选择 0、1、2 或 3 个）。确保你可以独立控制直方图和频率多边形的 binwidth。</p></li></ol><h2 id="103-使用代码创建用户界面"><a class="anchor" href="#103-使用代码创建用户界面">#</a> 10.3 使用代码创建用户界面</h2><p>有时，上面描述的技术无法提供您所需的动态性水平：更新函数只允许您更改现有的输入，而选项卡集仅在您具有固定且已知的可能组合集时才有效。有时，您需要根据其他输入创建不同类型的输入（或输出）或不同数量的输入（或输出）。这种最终技术使您能够这样做。</p><p>值得注意的是，您一直使用代码创建用户界面，但到目前为止，您总是在应用启动之前这样做。这种技术使您能够在应用运行时创建和修改用户界面。这个解决方案有两个部分：</p><ul><li><p><span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vaHRtbE91dHB1dC5odG1s">uiOutput()</span> 在用户界面 (ui) 中插入一个占位符。这留下了一个 “洞”，您的服务器代码稍后可以填充它。</p></li><li><p><span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vcmVuZGVyVUkuaHRtbA==">renderUI()</span> 在 <code>server()</code> 中被调用，用于将占位符填充为动态生成的用户界面。</p></li></ul><p>我们将通过一个简单的示例来了解这是如何工作的，然后深入探讨一些实际的应用。</p><h3 id="1031-入门"><a class="anchor" href="#1031-入门">#</a> 10.3.1 入门</h3><p>让我们从一个简单的应用开始，该应用动态地创建输入控件，其类型和标签由另外两个输入控件控制。最终的应用如图 <code>10.8</code> 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  textInput<span class="token punctuation">(</span><span class="token string">"label"</span><span class="token punctuation">,</span> <span class="token string">"label"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"type"</span><span class="token punctuation">,</span> <span class="token string">"type"</span><span class="token punctuation">,</span> c<span class="token punctuation">(</span><span class="token string">"slider"</span><span class="token punctuation">,</span> <span class="token string">"numeric"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  uiOutput<span class="token punctuation">(</span><span class="token string">"numeric"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  output<span class="token operator">$</span>numeric <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>type <span class="token operator">==</span> <span class="token string">"slider"</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>      sliderInput<span class="token punctuation">(</span><span class="token string">"dynamic"</span><span class="token punctuation">,</span> input<span class="token operator">$</span>label<span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="11"></td><td><pre>      numericInput<span class="token punctuation">(</span><span class="token string">"dynamic"</span><span class="token punctuation">,</span> input<span class="token operator">$</span>label<span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span> </pre></td></tr><tr><td data-num="12"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/c7a0fbee966861954422cd6a1d7fd4a39185ec9c/28819/demos/action-dynamic/render-simple-onload.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/35b772f26ba5a178d1f693ce7de4b12c17a454f3/9cb14/demos/action-dynamic/render-simple-numeric.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/d8a85a47b59618a13f336b7d9d0d7c705d2ef05e/9c957/demos/action-dynamic/render-simple-label.png"></td></tr><tr><td colspan="3">图10.8 应用加载时（左），然后将类型更改为数值（中），再将标签更改为“我的标签”。查看实时效果请访问 <span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXJlbmRlci1zaW1wbGU=">https://hadley.shinyapps.io/ms-render-simple</span></td></tr></table><p>如果你自己运行这段代码，你会发现应用在加载后需要一段时间才能显示。这是因为它是响应式的：应用必须先加载，触发一个响应事件，然后调用服务器函数，生成要插入页面的 HTML。这是<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vcmVuZGVyVUkuaHRtbA=="> renderUI ()</span> 的一个缺点；过多地依赖它可能会导致用户界面出现延迟。为了获得良好的性能，请尽量使用本章前面描述的技术，保持用户界面的固定部分。</p><p>这种方法还有一个问题：当你更改控件时，会丢失当前选定的值。在使用代码创建用户界面时，保留现有状态是一大挑战。这就是为什么如果适用，选择性显示和隐藏用户界面是更好的方法 —— 因为你没有销毁和重新创建控件，所以不需要做任何事情来保留值。然而，在许多情况下，我们可以通过将新输入的值设置为现有控件的当前值来解决这个问题：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  output<span class="token operator">$</span>numeric <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    value <span class="token operator">&lt;-</span> isolate<span class="token punctuation">(</span>input<span class="token operator">$</span>dynamic<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>type <span class="token operator">==</span> <span class="token string">"slider"</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>      sliderInput<span class="token punctuation">(</span><span class="token string">"dynamic"</span><span class="token punctuation">,</span> input<span class="token operator">$</span>label<span class="token punctuation">,</span> value <span class="token operator">=</span> value<span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      numericInput<span class="token punctuation">(</span><span class="token string">"dynamic"</span><span class="token punctuation">,</span> input<span class="token operator">$</span>label<span class="token punctuation">,</span> value <span class="token operator">=</span> value<span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>使用 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vaXNvbGF0ZS5odG1s">isolate()</span> 非常重要。我们将在 <code>15.4.1</code> 节中详细讨论它的作用，但在这里，它确保我们不会创建一个响应式依赖，导致每次 <code>input$dynamic</code> 发生变化时（每当用户修改值时都会发生）都重新运行此代码。我们只希望在 <code>input$type</code> 或 <code>input$label</code> 发生变化时更改它。</p><h3 id="1032-多个控件"><a class="anchor" href="#1032-多个控件">#</a> 10.3.2 多个控件</h3><p>当你需要生成任意数量或类型的控件时，动态用户界面（UI）最有用。这意味着你将使用代码生成用户界面，我建议使用函数式编程来完成此类任务。在这里，我将使用 <span class="exturl" data-url="aHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tYXAuaHRtbA==">purrr::map()</span> 和 <span class="exturl" data-url="aHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9yZWR1Y2UuaHRtbA==">purrr::reduce()</span>，但你也可以使用基础的 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9sYXBwbHkuaHRtbA==">lapply()</span> 和 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9mdW5wcm9nLmh0bWw=">Reduce()</span> 函数来实现同样的效果。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>library<span class="token punctuation">(</span>purrr<span class="token punctuation">)</span></pre></td></tr></table></figure><p>如果你不熟悉函数式编程中的 <span class="exturl" data-url="aHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tYXAuaHRtbA==">map()</span> 和 <code>reduce()</code> ，你可能希望在继续之前先阅读有关<span class="exturl" data-url="aHR0cHM6Ly9hZHYtci5oYWRsZXkubnovZnVuY3Rpb25hbHMuaHRtbA=="> Functional programming</span> 的内容。我们也将在第 <code>18</code> 章中再次讨论这个想法。这些想法相当复杂，所以如果你在第一次阅读时没有理解，请不要担心。</p><p>为了具体说明，想象一下你想要用户能够提供自己的颜色调色板。他们首先会指定他们想要的颜色的数量，然后为每个颜色提供一个值。用户界面相当简单：我们有一个 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vbnVtZXJpY0lucHV0Lmh0bWw=">numericInput()</span> 来控制输入的数量，一个 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vaHRtbE91dHB1dC5odG1s">uiOutput()</span> 用于放置生成的文本框，以及一个 <span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vdGV4dE91dHB1dC5odG1s">textOutput()</span> 来证明我们已经正确地将所有内容连接在一起。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Number of colours"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  uiOutput<span class="token punctuation">(</span><span class="token string">"col"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  textOutput<span class="token punctuation">(</span><span class="token string">"palette"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre><span class="token punctuation">)</span></pre></td></tr></table></figure><p>服务器函数很短，但包含了一些重要的想法：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  col_names <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span>paste0<span class="token punctuation">(</span><span class="token string">"col"</span><span class="token punctuation">,</span> seq_len<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  </pre></td></tr><tr><td data-num="4"></td><td><pre>  output<span class="token operator">$</span>col <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    map<span class="token punctuation">(</span>col_names<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> textInput<span class="token punctuation">(</span>.x<span class="token punctuation">,</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>  </pre></td></tr><tr><td data-num="8"></td><td><pre>  output<span class="token operator">$</span>palette <span class="token operator">&lt;-</span> renderText<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    map_chr<span class="token punctuation">(</span>col_names<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> input<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token percent-operator operator">%||%</span> <span class="token string">""</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><ul><li><p>我使用了一个响应式对象 <code>col_names()</code> 来存储即将生成的每个颜色输入的名称。</p></li><li><p>然后，我使用 <code>map()</code> 创建一个 <code>textInput()</code> 列表，每个列表项对应 <code>col_names()</code> 中的一个名称。 <code>renderUI()</code> 随后将这个 HTML 组件列表添加到用户界面。</p></li><li><p>我需要使用一个新技巧来访问输入值。到目前为止，我们总是使用 <code>$</code> 来访问输入的组件，例如 <code>input$col1</code> 。但在这里，我们的输入名称存储在一个字符向量中，比如 <code>var &lt;- &quot;col1&quot;</code> 。在这种情况下， <code>$</code> 不再适用，因此我们需要切换到 <code>[[</code> ，即 <code>input[[var]]</code> 。</p></li><li><p>我使用 <span class="exturl" data-url="aHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tYXAuaHRtbA==">map_chr()</span> 将所有值收集到一个字符向量中，并在 <code>output$palette</code> 中显示。不幸的是，在浏览器渲染新输入之前，有一个短暂的时间段，其值会是 <code>NULL</code> 。这会导致 <code>map_chr()</code> 报错，我们使用方便的 <code>%||%</code> 函数来解决这个问题：当左侧为 <code>NULL</code> 时，它返回右侧的值。</p></li></ul><p>你可以在图 <code>10.9</code> 中看到结果。</p><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/5301c856f06d8eba2b98b4a7c4d3fedb574d7f11/76c4e/demos/action-dynamic/render-palette-onload.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/6a2df96ad06c05e809774c0db7d7e3611ec9eb53/d31b7/demos/action-dynamic/render-palette-change-n.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/cad6373c73a55bf6e79fc166e72dadf394b528e1/b0691/demos/action-dynamic/render-palette-set-cols.png"></td></tr><tr><td colspan="3">图10.9 应用程序加载时（左），将n设置为3后（中），然后输入一些颜色（右）。请在<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXJlbmRlci1wYWxldHRl">https://hadley.shinyapps.io/ms-render-palette</span>查看实时效果</td></tr></table><p>如果你运行这个应用程序，你会发现一个非常恼人的行为：每当你改变颜色的数量时，所有输入的数据都会消失。我们可以使用与之前相同的技术来解决这个问题：将 <code>value</code> 设置为（孤立的）当前值。我还会稍微调整外观，使其看起来更漂亮一些，包括在图中显示所选颜色。示例截图如图 <code>10.10</code> 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>      numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"Number of colours"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">5</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>      uiOutput<span class="token punctuation">(</span><span class="token string">"col"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="8"></td><td><pre>      plotOutput<span class="token punctuation">(</span><span class="token string">"plot"</span><span class="token punctuation">)</span>  </pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>  col_names <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span>paste0<span class="token punctuation">(</span><span class="token string">"col"</span><span class="token punctuation">,</span> seq_len<span class="token punctuation">(</span>input<span class="token operator">$</span>n<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  </pre></td></tr><tr><td data-num="16"></td><td><pre>  output<span class="token operator">$</span>col <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>    map<span class="token punctuation">(</span>col_names<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> textInput<span class="token punctuation">(</span>.x<span class="token punctuation">,</span> <span class="token keyword">NULL</span><span class="token punctuation">,</span> value <span class="token operator">=</span> isolate<span class="token punctuation">(</span>input<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  </pre></td></tr><tr><td data-num="20"></td><td><pre>  output<span class="token operator">$</span>plot <span class="token operator">&lt;-</span> renderPlot<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    cols <span class="token operator">&lt;-</span> map_chr<span class="token punctuation">(</span>col_names<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> input<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token percent-operator operator">%||%</span> <span class="token string">""</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre>    <span class="token comment"># convert empty inputs to transparent</span></pre></td></tr><tr><td data-num="23"></td><td><pre>    cols<span class="token punctuation">[</span>cols <span class="token operator">==</span> <span class="token string">""</span><span class="token punctuation">]</span> <span class="token operator">&lt;-</span> <span class="token keyword">NA</span></pre></td></tr><tr><td data-num="24"></td><td><pre>    </pre></td></tr><tr><td data-num="25"></td><td><pre>    barplot<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="26"></td><td><pre>      rep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> length<span class="token punctuation">(</span>cols<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="27"></td><td><pre>      col <span class="token operator">=</span> cols<span class="token punctuation">,</span></pre></td></tr><tr><td data-num="28"></td><td><pre>      space <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> </pre></td></tr><tr><td data-num="29"></td><td><pre>      axes <span class="token operator">=</span> <span class="token boolean">FALSE</span></pre></td></tr><tr><td data-num="30"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="31"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">,</span> res <span class="token operator">=</span> <span class="token number">96</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="32"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><table><tr><td><img data-src="https://d33wubrfki0l68.cloudfront.net/cc1d6ffde1b8ac88f72e2b897ed187f55cecedf1/89026/demos/action-dynamic/render-palette-full-rainbow.png"></td><td><img data-src="https://d33wubrfki0l68.cloudfront.net/b1560598089d41fb8a7c71323c44efd7595ce615/b8927/demos/action-dynamic/render-palette-full-change-n.png"></td></tr><tr><td colspan="2">图10.10 填写彩虹的颜色（左），然后将颜色数量减少到3（右）；请注意，现有颜色被保留。实时效果请访问<span class="exturl" data-url="aHR0cHM6Ly9oYWRsZXkuc2hpbnlhcHBzLmlvL21zLXJlbmRlci1wYWxldHRlLWZ1bGw=">https://hadley.shinyapps.io/ms-render-palette-full</span>查看实时效果</td></tr></table><h3 id="1033-动态过滤"><a class="anchor" href="#1033-动态过滤">#</a> 10.3.3 动态过滤</h3><p>为了结束本章，我将创建一个应用程序，允许你动态过滤任何数据框。每个数值变量都会得到一个范围滑块，每个因子变量都会得到一个多选控件，所以（例如）如果一个数据框有三个数值变量和两个因子，应用程序将有三个滑块和两个选择框。</p><p>我将从一个为单个变量创建用户界面的函数开始。对于数值输入，它将返回一个范围滑块；对于因子输入，它将返回一个多选控件；对于其他所有类型，它将返回 NULL（无）。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>make_ui <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> var<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  <span class="token keyword">if</span> <span class="token punctuation">(</span>is.numeric<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    rng <span class="token operator">&lt;-</span> range<span class="token punctuation">(</span>x<span class="token punctuation">,</span> na.rm <span class="token operator">=</span> <span class="token boolean">TRUE</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre>    sliderInput<span class="token punctuation">(</span>var<span class="token punctuation">,</span> var<span class="token punctuation">,</span> min <span class="token operator">=</span> rng<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> max <span class="token operator">=</span> rng<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span> value <span class="token operator">=</span> rng<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>is.factor<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    levs <span class="token operator">&lt;-</span> levels<span class="token punctuation">(</span>x<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    selectInput<span class="token punctuation">(</span>var<span class="token punctuation">,</span> var<span class="token punctuation">,</span> choices <span class="token operator">=</span> levs<span class="token punctuation">,</span> selected <span class="token operator">=</span> levs<span class="token punctuation">,</span> multiple <span class="token operator">=</span> <span class="token boolean">TRUE</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token comment"># Not supported</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token keyword">NULL</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>接着，我将编写此函数的服务器端等效项：它接收输入控件的变量和值，并返回一个逻辑向量，说明是否包含每个观测值。使用逻辑向量可以轻松组合来自多个列的结果。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>filter_var <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>x<span class="token punctuation">,</span> val<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  <span class="token keyword">if</span> <span class="token punctuation">(</span>is.numeric<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    <span class="token operator">!</span>is.na<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token operator">&amp;</span> x <span class="token operator">>=</span> val<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">&amp;</span> x <span class="token operator">&lt;=</span> val<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>is.factor<span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    x <span class="token percent-operator operator">%in%</span> val</pre></td></tr><tr><td data-num="6"></td><td><pre>  <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token comment"># No control, so don't filter</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token boolean">TRUE</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>然后，我可以手动使用这些函数为 <code>iris</code> 数据集生成一个简单的过滤用户界面：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>      make_ui<span class="token punctuation">(</span>iris<span class="token operator">$</span>Sepal.Length<span class="token punctuation">,</span> <span class="token string">"Sepal.Length"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="5"></td><td><pre>      make_ui<span class="token punctuation">(</span>iris<span class="token operator">$</span>Sepal.Width<span class="token punctuation">,</span> <span class="token string">"Sepal.Width"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>      make_ui<span class="token punctuation">(</span>iris<span class="token operator">$</span>Species<span class="token punctuation">,</span> <span class="token string">"Species"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="9"></td><td><pre>      tableOutput<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>  selected <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>    filter_var<span class="token punctuation">(</span>iris<span class="token operator">$</span>Sepal.Length<span class="token punctuation">,</span> input<span class="token operator">$</span>Sepal.Length<span class="token punctuation">)</span> <span class="token operator">&amp;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>      filter_var<span class="token punctuation">(</span>iris<span class="token operator">$</span>Sepal.Width<span class="token punctuation">,</span> input<span class="token operator">$</span>Sepal.Width<span class="token punctuation">)</span> <span class="token operator">&amp;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>      filter_var<span class="token punctuation">(</span>iris<span class="token operator">$</span>Species<span class="token punctuation">,</span> input<span class="token operator">$</span>Species<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  </pre></td></tr><tr><td data-num="20"></td><td><pre>  output<span class="token operator">$</span>data <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>head<span class="token punctuation">(</span>iris<span class="token punctuation">[</span>selected<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="21"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/c346538e80dd7b34a8142bdb05a537b44d581663/edd79/demos/action-dynamic/render-filter-1.png" alt="图10.11 iris数据集的简单过滤界面"></p><p>你可能注意到了，我已经厌倦了复制粘贴，所以这个应用程序只适用于三列。通过使用一些函数式编程，我可以让它适用于所有列：</p><ul><li><p>在 <code>ui</code> 中，使用<span class="exturl" data-url="aHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9tYXAuaHRtbA=="> map ()</span> 为每个变量生成一个控件。</p></li><li><p>在 <code>server()</code> ，我使用 <code>map()</code> 为每个变量生成选择向量。然后，我使用 <code>reduce()</code> 将每个变量的逻辑向量组合成一个单一的逻辑向量，通过 <code>&amp;</code> 将每个向量连接在一起。</p></li></ul><p>再次强调，如果你不完全理解这里发生了什么，请不要太过担心。主要的收获是，一旦你掌握了函数式编程，你就可以编写非常简洁的代码，从而生成复杂且动态的应用程序。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="3"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>      map<span class="token punctuation">(</span>names<span class="token punctuation">(</span>iris<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> make_ui<span class="token punctuation">(</span>iris<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> .x<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="6"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      tableOutput<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="10"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  selected <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="13"></td><td><pre>    each_var <span class="token operator">&lt;-</span> map<span class="token punctuation">(</span>names<span class="token punctuation">(</span>iris<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> filter_var<span class="token punctuation">(</span>iris<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> input<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    reduce<span class="token punctuation">(</span>each_var<span class="token punctuation">,</span> <span class="token operator">~</span> .x <span class="token operator">&amp;</span> .y<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="16"></td><td><pre>  </pre></td></tr><tr><td data-num="17"></td><td><pre>  output<span class="token operator">$</span>data <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>head<span class="token punctuation">(</span>iris<span class="token punctuation">[</span>selected<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/b5f7aa079010dea111f8cb50ff9ce875cef92b4a/4b175/demos/action-dynamic/render-filter-2.png" alt="图10.12 使用函数式编程为iris数据集构建过滤应用程序"></p><p>从此处开始，对其进行简单泛化，使其可以与任何数据框配合使用。在此，我将使用 datasets 包中的数据框进行说明，但你可以很容易地想象如何将其扩展到用户上传的数据。结果如图 <code>10.13</code> 所示。</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>dfs <span class="token operator">&lt;-</span> keep<span class="token punctuation">(</span>ls<span class="token punctuation">(</span><span class="token string">"package:datasets"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> is.data.frame<span class="token punctuation">(</span>get<span class="token punctuation">(</span>.x<span class="token punctuation">,</span> <span class="token string">"package:datasets"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="2"></td><td><pre></pre></td></tr><tr><td data-num="3"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="4"></td><td><pre>  sidebarLayout<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="5"></td><td><pre>    sidebarPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="6"></td><td><pre>      selectInput<span class="token punctuation">(</span><span class="token string">"dataset"</span><span class="token punctuation">,</span> label <span class="token operator">=</span> <span class="token string">"Dataset"</span><span class="token punctuation">,</span> choices <span class="token operator">=</span> dfs<span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="7"></td><td><pre>      uiOutput<span class="token punctuation">(</span><span class="token string">"filter"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="8"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    mainPanel<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="10"></td><td><pre>      tableOutput<span class="token punctuation">(</span><span class="token string">"data"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="14"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>  data <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    get<span class="token punctuation">(</span>input<span class="token operator">$</span>dataset<span class="token punctuation">,</span> <span class="token string">"package:datasets"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="17"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="18"></td><td><pre>  vars <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span>names<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  </pre></td></tr><tr><td data-num="20"></td><td><pre>  output<span class="token operator">$</span>filter <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="21"></td><td><pre>    map<span class="token punctuation">(</span>vars<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> make_ui<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> .x<span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="22"></td><td><pre>  <span class="token punctuation">)</span></pre></td></tr><tr><td data-num="23"></td><td><pre>  </pre></td></tr><tr><td data-num="24"></td><td><pre>  selected <span class="token operator">&lt;-</span> reactive<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="25"></td><td><pre>    each_var <span class="token operator">&lt;-</span> map<span class="token punctuation">(</span>vars<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">~</span> filter_var<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span> input<span class="token punctuation">[</span><span class="token punctuation">[</span>.x<span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="26"></td><td><pre>    reduce<span class="token punctuation">(</span>each_var<span class="token punctuation">,</span> `<span class="token operator">&amp;</span>`<span class="token punctuation">)</span></pre></td></tr><tr><td data-num="27"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="28"></td><td><pre>  </pre></td></tr><tr><td data-num="29"></td><td><pre>  output<span class="token operator">$</span>data <span class="token operator">&lt;-</span> renderTable<span class="token punctuation">(</span>head<span class="token punctuation">(</span>data<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span>selected<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="30"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p><img data-src="https://d33wubrfki0l68.cloudfront.net/539c4bcb59b7d52133c346ec6ca4a217e122e4df/9214d/demos/action-dynamic/filtering-final.png" alt="图10.13 根据所选数据集的字段自动生成的动态用户界面。请访问https://hadley.shinyapps.io/ms-filtering-final查看实时效果"></p><h3 id="1034-对话框"><a class="anchor" href="#1034-对话框">#</a> 10.3.4 对话框</h3><p>在结束本章之前，我想提一下一个相关的技术：对话框。在 <code>8.4.1</code> 节中，你已经看到了对话框，其内容是固定的文本字符串。但由于<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vbW9kYWxEaWFsb2cuaHRtbA=="> modalDialog ()</span> 是在服务器函数中调用的，因此你可以像<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vcmVuZGVyVUkuaHRtbA=="> renderUI ()</span> 一样动态地生成内容。如果你想在继续常规应用程序流程之前强制用户做出某些决定，这是一个很有用的技术。</p><h3 id="1035-练习"><a class="anchor" href="#1035-练习">#</a> 10.3.5 练习</h3><ol><li><p>根据本节中的初始示例，创建一个非常简单的应用程序：</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  selectInput<span class="token punctuation">(</span><span class="token string">"type"</span><span class="token punctuation">,</span> <span class="token string">"type"</span><span class="token punctuation">,</span> c<span class="token punctuation">(</span><span class="token string">"slider"</span><span class="token punctuation">,</span> <span class="token string">"numeric"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  uiOutput<span class="token punctuation">(</span><span class="token string">"numeric"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  output<span class="token operator">$</span>numeric <span class="token operator">&lt;-</span> renderUI<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span>input<span class="token operator">$</span>type <span class="token operator">==</span> <span class="token string">"slider"</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="8"></td><td><pre>      sliderInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"n"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="9"></td><td><pre>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="10"></td><td><pre>      numericInput<span class="token punctuation">(</span><span class="token string">"n"</span><span class="token punctuation">,</span> <span class="token string">"n"</span><span class="token punctuation">,</span> value <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> min <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span> max <span class="token operator">=</span> <span class="token number">100</span><span class="token punctuation">)</span>  </pre></td></tr><tr><td data-num="11"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="12"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="13"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure><p>你如何使用动态可见性来实现它？如果你实现了动态可见性，当你更改控件时，如何保持值的同步？</p></li><li><p>解释这个应用程序是如何工作的。为什么当你第二次点击 “输入密码” 按钮时，密码会消失？</p><figure class="highlight r"><figcaption data-lang="r"></figcaption><table><tr><td data-num="1"></td><td><pre>ui <span class="token operator">&lt;-</span> fluidPage<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="2"></td><td><pre>  actionButton<span class="token punctuation">(</span><span class="token string">"go"</span><span class="token punctuation">,</span> <span class="token string">"Enter password"</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="3"></td><td><pre>  textOutput<span class="token punctuation">(</span><span class="token string">"text"</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="4"></td><td><pre><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="5"></td><td><pre>server <span class="token operator">&lt;-</span> <span class="token keyword">function</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> output<span class="token punctuation">,</span> session<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="6"></td><td><pre>  observeEvent<span class="token punctuation">(</span>input<span class="token operator">$</span>go<span class="token punctuation">,</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="7"></td><td><pre>    showModal<span class="token punctuation">(</span>modalDialog<span class="token punctuation">(</span></pre></td></tr><tr><td data-num="8"></td><td><pre>      passwordInput<span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">,</span> <span class="token keyword">NULL</span><span class="token punctuation">)</span><span class="token punctuation">,</span></pre></td></tr><tr><td data-num="9"></td><td><pre>      title <span class="token operator">=</span> <span class="token string">"Please enter your password"</span></pre></td></tr><tr><td data-num="10"></td><td><pre>    <span class="token punctuation">)</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="11"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="12"></td><td><pre></pre></td></tr><tr><td data-num="13"></td><td><pre>  output<span class="token operator">$</span>text <span class="token operator">&lt;-</span> renderText<span class="token punctuation">(</span><span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="14"></td><td><pre>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>isTruthy<span class="token punctuation">(</span>input<span class="token operator">$</span>password<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="15"></td><td><pre>      <span class="token string">"No password"</span></pre></td></tr><tr><td data-num="16"></td><td><pre>    <span class="token punctuation">&#125;</span> <span class="token keyword">else</span> <span class="token punctuation">&#123;</span></pre></td></tr><tr><td data-num="17"></td><td><pre>      <span class="token string">"Password entered"</span></pre></td></tr><tr><td data-num="18"></td><td><pre>    <span class="token punctuation">&#125;</span></pre></td></tr><tr><td data-num="19"></td><td><pre>  <span class="token punctuation">&#125;</span><span class="token punctuation">)</span></pre></td></tr><tr><td data-num="20"></td><td><pre><span class="token punctuation">&#125;</span></pre></td></tr></table></figure></li><li><p>在・10.3.1・节的应用程序中，如果你从 <code>value &lt;- isolate(input$dynamic)</code> 中去掉<span class="exturl" data-url="aHR0cHM6Ly9yZHJyLmlvL3BrZy9zaGlueS9tYW4vaXNvbGF0ZS5odG1s"> isolate ()</span> 会发生什么？</p></li><li><p>为 <code>make_ui()</code> 和 <code>filter_var()</code> 添加对日期和日期时间列的支持。</p></li><li><p>（高级）如果你了解 S3 面向对象编程（<span class="exturl" data-url="aHR0cDovL2Fkdi1yLmhhZGxleS5uei9TMy5odG1s">S3 OOP</span>）系统，请考虑如何使用通用函数替换 <code>make_ui()</code> 和 <code>filter_var()</code> 中的 if 块。</p></li></ol><h2 id="104-总结"><a class="anchor" href="#104-总结">#</a> 10.4 总结</h2><p>在阅读本章之前，你只能在服务器函数运行之前静态地创建用户界面。现在你已经学会了如何根据用户操作修改用户界面并完全重新创建它。动态用户界面将极大地增加你的应用程序的复杂性，所以如果你发现自己很难调试正在发生的事情，不要感到惊讶。始终记住使用最简单的技术来解决你的问题，并回到 <code>5.2</code> 节中的调试建议。</p><p>下一章将转向讨论书签功能，使应用程序能够与他人共享当前状态。</p><h1 id="加关注"><a class="anchor" href="#加关注">#</a> 加关注</h1><p>关注公众号 “生信之巅”。</p><table align="center"><tr><td align="center"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/social/生信之巅公众号.jpg" alt="生信之巅微信公众号" style="width:100px;height:100px;vertical-align:-20px;border-radius:0;margin-right:0;margin-bottom:5px;align:center"></td><td align="center"><img data-src="https://cdn.jsdelivr.net/gh/liaochenlanruo/cdn@master/img/social/小程序码.png" alt="生信之巅小程序码" style="width:100px;height:100px;vertical-align:-20px;border-radius:0;margin-left:0;margin-bottom:5px;align:center"></td></tr></table><p><font color="#FF0000"><ruby><b>敬告</b>：使用文中脚本请引用本文网址，请尊重本人的劳动成果，谢谢！<rt><b>Notice</b>: When you use the scripts in this article, please cite the link of this webpage. Thank you!</rt></ruby></font></p><div class="tags"><a href="/tags/%E7%BC%96%E7%A8%8B/" rel="tag"><i class="ic i-tag"></i> 编程</a> <a href="/tags/Shiny%E5%85%A5%E9%97%A8%E7%B3%BB%E5%88%97/" rel="tag"><i class="ic i-tag"></i> Shiny入门系列</a></div></div><footer><div class="meta"><span class="item"><span class="icon"><i class="ic i-calendar-check"></i> </span><span class="text">Edited on</span> <time title="Modified: 2024-04-27 21:53:00" itemprop="dateModified" datetime="2024-04-27T21:53:00+08:00">2024-04-27</time> </span><span id="post/e6be.html" class="item leancloud_visitors" data-flag-title="Shiny从入门到入定——10-动态UI" title="Views"><span class="icon"><i class="ic i-eye"></i> </span><span class="text">Views</span> <span class="leancloud-visitors-count"></span> <span class="text">times</span></span></div><div class="reward"><button><i class="ic i-heartbeat"></i> Donate</button><p>Give me a cup of [coffee]~(￣▽￣)~*</p><div id="qr"><div><img data-src="/images/reward-wepays.jpg" alt="Hualin Liu WeChat Pay"><p>WeChat Pay</p></div><div><img data-src="/images/AliPays.jpg" alt="Hualin Liu Alipay"><p>Alipay</p></div></div></div><div id="copyright"><ul><li class="author"><strong>Post author: </strong>liaochenlanruo <i class="ic i-at"><em>@</em></i>了尘兰若的小坑</li><li class="link"><strong>Post link: </strong><a href="https://liaochenlanruo.gitee.io/post/e6be.html" title="Shiny从入门到入定——10-动态UI">https://liaochenlanruo.gitee.io/post/e6be.html</a></li><li class="license"><strong>Copyright Notice: </strong>All articles in this blog are licensed under <span class="exturl" data-url="aHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMC9kZWVkLnpo"><i class="ic i-creative-commons"><em>(CC)</em></i>BY-NC-SA</span> unless stating additionally.</li></ul></div></footer></article></div><div class="post-nav"><div class="item left"><a href="/post/72d6.html" itemprop="url" rel="prev" data-background-image="https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;gh&#x2F;liaochenlanruo&#x2F;cdn@master&#x2F;img&#x2F;custom&#x2F;bgs&#x2F;thumb_209.webp" title="Shiny从入门到入定——9-上传和下载"><span class="type">Previous Post</span> <span class="category"><i class="ic i-flag"></i> IT</span><h3>Shiny从入门到入定——9-上传和下载</h3></a></div><div class="item right"><a href="/post/8eca.html" itemprop="url" rel="next" data-background-image="https:&#x2F;&#x2F;cdn.jsdelivr.net&#x2F;gh&#x2F;liaochenlanruo&#x2F;cdn@master&#x2F;img&#x2F;custom&#x2F;bgs&#x2F;thumb_211.webp" title="Shiny从入门到入定——11-书签"><span class="type">Next Post</span> <span class="category"><i class="ic i-flag"></i> IT</span><h3>Shiny从入门到入定——11-书签</h3></a></div></div><div class="wrap" id="comments"></div></div><div id="sidebar"><div class="inner"><div class="panels"><div class="inner"><div class="contents panel pjax" data-title="Contents"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#10-%E5%8A%A8%E6%80%81ui"><span class="toc-number">1.</span> <span class="toc-text">10 动态 UI</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#101-%E6%9B%B4%E6%96%B0%E8%BE%93%E5%85%A5"><span class="toc-number">1.1.</span> <span class="toc-text">10.1 更新输入</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1011-%E7%AE%80%E5%8D%95%E5%BA%94%E7%94%A8"><span class="toc-number">1.1.1.</span> <span class="toc-text">10.1.1 简单应用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1012-%E5%88%86%E5%B1%82%E9%80%89%E6%8B%A9%E6%A1%86"><span class="toc-number">1.1.2.</span> <span class="toc-text">10.1.2 分层选择框</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1013-%E5%86%BB%E7%BB%93%E5%93%8D%E5%BA%94%E5%BC%8F%E8%BE%93%E5%85%A5"><span class="toc-number">1.1.3.</span> <span class="toc-text">10.1.3 冻结响应式输入</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1014-%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8"><span class="toc-number">1.1.4.</span> <span class="toc-text">10.1.4 循环引用</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1015-%E7%9B%B8%E4%BA%92%E5%85%B3%E8%81%94%E7%9A%84%E8%BE%93%E5%85%A5"><span class="toc-number">1.1.5.</span> <span class="toc-text">10.1.5 相互关联的输入</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1016-%E7%BB%83%E4%B9%A0"><span class="toc-number">1.1.6.</span> <span class="toc-text">10.1.6 练习</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#102-%E5%8A%A8%E6%80%81%E5%8F%AF%E8%A7%81%E6%80%A7"><span class="toc-number">1.2.</span> <span class="toc-text">10.2 动态可见性</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1021-%E6%9D%A1%E4%BB%B6%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2"><span class="toc-number">1.2.1.</span> <span class="toc-text">10.2.1 条件用户界面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1022-%E5%90%91%E5%AF%BC%E7%95%8C%E9%9D%A2"><span class="toc-number">1.2.2.</span> <span class="toc-text">10.2.2 向导界面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1023-%E7%BB%83%E4%B9%A0"><span class="toc-number">1.2.3.</span> <span class="toc-text">10.2.3 练习</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#103-%E4%BD%BF%E7%94%A8%E4%BB%A3%E7%A0%81%E5%88%9B%E5%BB%BA%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2"><span class="toc-number">1.3.</span> <span class="toc-text">10.3 使用代码创建用户界面</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1031-%E5%85%A5%E9%97%A8"><span class="toc-number">1.3.1.</span> <span class="toc-text">10.3.1 入门</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1032-%E5%A4%9A%E4%B8%AA%E6%8E%A7%E4%BB%B6"><span class="toc-number">1.3.2.</span> <span class="toc-text">10.3.2 多个控件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1033-%E5%8A%A8%E6%80%81%E8%BF%87%E6%BB%A4"><span class="toc-number">1.3.3.</span> <span class="toc-text">10.3.3 动态过滤</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1034-%E5%AF%B9%E8%AF%9D%E6%A1%86"><span class="toc-number">1.3.4.</span> <span class="toc-text">10.3.4 对话框</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#1035-%E7%BB%83%E4%B9%A0"><span class="toc-number">1.3.5.</span> <span class="toc-text">10.3.5 练习</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#104-%E6%80%BB%E7%BB%93"><span class="toc-number">1.4.</span> <span class="toc-text">10.4 总结</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%8A%A0%E5%85%B3%E6%B3%A8"><span class="toc-number">2.</span> <span class="toc-text">加关注</span></a></li></ol></div><div class="related panel pjax" data-title="Related"><ul><li><a href="/post/10103.html" rel="bookmark" title="perl循环调用python爬虫批量下载喜马拉雅音频">perl循环调用python爬虫批量下载喜马拉雅音频</a></li><li><a href="/post/2159.html" rel="bookmark" title="syncthing搭建自己的同步云网盘">syncthing搭建自己的同步云网盘</a></li><li><a href="/post/50435.html" rel="bookmark" title="JAVA jre 安装或升级1603 error解决办法">JAVA jre 安装或升级1603 error解决办法</a></li><li><a href="/post/aa20.html" rel="bookmark" title="获取下一版本的内部预览版windows系统">获取下一版本的内部预览版windows系统</a></li><li><a href="/post/c78e.html" rel="bookmark" title="windows 10 中文乱码解决方案">windows 10 中文乱码解决方案</a></li><li><a href="/post/6bf1.html" rel="bookmark" title="Ubuntu open-mpi运行错误">Ubuntu open-mpi运行错误</a></li><li><a href="/post/f1a4.html" rel="bookmark" title="Github操作">Github操作</a></li><li><a href="/post/f298.html" rel="bookmark" title="Wget 技巧">Wget 技巧</a></li><li><a href="/post/16e1.html" rel="bookmark" title="Perl捕获外部命令执行异常并继续运行后续代码">Perl捕获外部命令执行异常并继续运行后续代码</a></li><li><a href="/post/9092.html" rel="bookmark" title="Perl获取外部命令执行结果的输出">Perl获取外部命令执行结果的输出</a></li><li><a href="/post/9d1b.html" rel="bookmark" title="Linux骚操作">Linux骚操作</a></li><li><a href="/post/3f45.html" rel="bookmark" title="Perl处理可恶的Windows换行符">Perl处理可恶的Windows换行符</a></li><li><a href="/post/893d.html" rel="bookmark" title="命令模式下配置Linux网络">命令模式下配置Linux网络</a></li><li><a href="/post/ed43.html" rel="bookmark" title="Linux中使用tar将大文件压缩为多个小的压缩包">Linux中使用tar将大文件压缩为多个小的压缩包</a></li><li><a href="/post/54a0.html" rel="bookmark" title="WSL安装Docker避坑指北">WSL安装Docker避坑指北</a></li><li><a href="/post/4b9c.html" rel="bookmark" title="Shiny从入门到入定——0欢迎入坑">Shiny从入门到入定——0欢迎入坑</a></li><li><a href="/post/b376.html" rel="bookmark" title="Shiny从入门到入定——1.在小小的花园里面挖呀挖呀挖">Shiny从入门到入定——1.在小小的花园里面挖呀挖呀挖</a></li><li><a href="/post/5369.html" rel="bookmark" title="Shiny从入门到入定——2-在大大的花园里面挖呀挖呀挖">Shiny从入门到入定——2-在大大的花园里面挖呀挖呀挖</a></li><li><a href="/post/41c3.html" rel="bookmark" title="Shiny从入门到入定——3-在特别大的花园里面挖呀挖呀挖">Shiny从入门到入定——3-在特别大的花园里面挖呀挖呀挖</a></li><li><a href="/post/377b.html" rel="bookmark" title="Shiny从入门到入定——4-挖坑完毕之案例研究">Shiny从入门到入定——4-挖坑完毕之案例研究</a></li><li><a href="/post/83c3.html" rel="bookmark" title="Shiny从入门到入定——5-工作流">Shiny从入门到入定——5-工作流</a></li><li><a href="/post/6718.html" rel="bookmark" title="Shiny从入门到入定——6-布局、主题、HTML">Shiny从入门到入定——6-布局、主题、HTML</a></li><li><a href="/post/6685.html" rel="bookmark" title="Shiny从入门到入定——7-图形">Shiny从入门到入定——7-图形</a></li><li><a href="/post/6561.html" rel="bookmark" title="Shiny从入门到入定——8-用户反馈">Shiny从入门到入定——8-用户反馈</a></li><li><a href="/post/72d6.html" rel="bookmark" title="Shiny从入门到入定——9-上传和下载">Shiny从入门到入定——9-上传和下载</a></li><li class="active"><a href="/post/e6be.html" rel="bookmark" title="Shiny从入门到入定——10-动态UI">Shiny从入门到入定——10-动态UI</a></li><li><a href="/post/8eca.html" rel="bookmark" title="Shiny从入门到入定——11-书签">Shiny从入门到入定——11-书签</a></li><li><a href="/post/3709.html" rel="bookmark" title="Shiny从入门到入定——12-Tidy evaluation">Shiny从入门到入定——12-Tidy evaluation</a></li><li><a href="/post/5e3d.html" rel="bookmark" title="Shiny从入门到入定——13-Why reactivity?">Shiny从入门到入定——13-Why reactivity?</a></li><li><a href="/post/43fc.html" rel="bookmark" title="深入理解特征标准化：为何、如何及其重要性">深入理解特征标准化：为何、如何及其重要性</a></li></ul></div><div class="overview panel" data-title="Overview"><div class="author" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="image" itemprop="image" alt="Hualin Liu" data-src="/images/head.jpg"><p class="name" itemprop="name">Hualin Liu</p><div class="description" itemprop="description">分享微生物生物信息学分析方法，欢迎加入QQ群交流945751012，不接受群内广告！</div></div><nav class="state"><div class="item posts"><a href="/archives/"><span class="count">130</span> <span class="name">posts</span></a></div><div class="item categories"><a href="/categories/"><span class="count">12</span> <span class="name">categories</span></a></div><div class="item tags"><a href="/tags/"><span class="count">54</span> <span class="name">tags</span></a></div></nav><div class="social"><span class="exturl item github" data-url="aHR0cDovL2dpdGh1Yi5jb20vbGlhb2NoZW5sYW5ydW8=" title="http:&#x2F;&#x2F;github.com&#x2F;liaochenlanruo"><i class="ic i-github"></i></span> <span class="exturl item zhihu" data-url="aHR0cHM6Ly93d3cuemhpaHUuY29tL3Blb3BsZS9zdWFuLWxhLW1pYW4tcGlhbi10YW5nLWNpdW0=" title="https:&#x2F;&#x2F;www.zhihu.com&#x2F;people&#x2F;suan-la-mian-pian-tang-cium"><i class="ic i-zhihu"></i></span> <span class="exturl item weibo" data-url="aHR0cHM6Ly93ZWliby5jb20vdS8yNzE1MjEwOTIz" title="https:&#x2F;&#x2F;weibo.com&#x2F;u&#x2F;2715210923"><i class="ic i-weibo"></i></span> <span class="exturl item about" data-url="aHR0cHM6Ly93d3cubGlhb2NoZW5sYW5ydW8uZnVuL2Fib3V0" title="https:&#x2F;&#x2F;www.liaochenlanruo.fun&#x2F;about"><i class="ic i-address-card"></i></span> <span class="exturl item email" data-url="bWFpbHRvOmxpYW9jaGVubGFucnVvQHdlYm1haWwuaHphdS5lZHUuY24=" title="mailto:liaochenlanruo@webmail.hzau.edu.cn"><i class="ic i-envelope"></i></span> <span class="exturl item douban" data-url="aHR0cHM6Ly93d3cuZG91YmFuLmNvbS9wZW9wbGUvbGlhb2NoZW5sYW5ydW8vP19pPTczNDYxMjVRalo1QjZWLw==" title="https:&#x2F;&#x2F;www.douban.com&#x2F;people&#x2F;liaochenlanruo&#x2F;?_i&#x3D;7346125QjZ5B6V&#x2F;"><i class="ic i-douban"></i></span> <span class="exturl item google" data-url="aHR0cHM6Ly9zY2hvbGFyLmdvb2dsZS5jby5qcC9jaXRhdGlvbnM/dXNlcj1YYW9STk1RQUFBQUomaGw9amE=" title="https:&#x2F;&#x2F;scholar.google.co.jp&#x2F;citations?user&#x3D;XaoRNMQAAAAJ&amp;hl&#x3D;ja"><i class="ic i-google"></i></span></div><ul class="menu"><li class="item"><a href="/" rel="section"><i class="ic i-home"></i>Home</a></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-user"></i>About</a><ul class="submenu"><li class="item"><a href="/about/" rel="section"><i class="ic i-user"></i>About me</a></li><li class="item"><a href="/comment/" rel="section"><i class="ic i-envelope"></i>Comment</a></li></ul></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-feather"></i>Posts</a><ul class="submenu"><li class="item"><a href="/archives/" rel="section"><i class="ic i-list-alt"></i>Archives</a></li><li class="item"><a href="/categories/" rel="section"><i class="ic i-th"></i>Categories</a></li><li class="item"><a href="/tags/" rel="section"><i class="ic i-tags"></i>Tags</a></li></ul></li><li class="item dropdown"><a href="javascript:void(0);"><i class="ic i-list-ol"></i>List</a><ul class="submenu"><li class="item"><a href="/photos/" rel="section"><i class="ic i-person"></i>Photos</a></li><li class="item"><a href="/skip/gallery/" rel="section"><i class="ic i-cloud"></i>Gallery</a></li><li class="item"><a href="/skip/box/" rel="section"><i class="ic i-magic"></i>Box</a></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly9saWFvY2hlbmxhbnJ1by5mdW4vcmVmZXJlbmNlLw=="><i class="ic i-th"></i>cheatsheets</span></li></ul></li><li class="item"><a href="/donate/" rel="section"><i class="ic i-coffee"></i>Donate</a></li><li class="item"><a href="/friends/" rel="section"><i class="ic i-heart"></i>Friends</a></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly9saWFvY2hlbmxhbnJ1by5naXRodWIuaW8="><i class="ic i-external-link-alt"></i>中文版</span></li><li class="item"><span class="exturl" data-url="aHR0cHM6Ly90cmF2ZWxsaW5ncy5saW5r"><i class="ic i-forward"></i>Travelling</span></li></ul></div></div></div><ul id="quick"><li class="prev pjax"><a href="/post/72d6.html" rel="prev" title="Previous Post"><i class="ic i-chevron-left"></i></a></li><li class="up"><i class="ic i-arrow-up"></i></li><li class="down"><i class="ic i-arrow-down"></i></li><li class="next pjax"><a href="/post/8eca.html" rel="next" title="Next Post"><i class="ic i-chevron-right"></i></a></li><li class="percent"></li></ul></div></div><div class="dimmer"></div></div></main><footer id="footer"><div class="inner"><div class="widgets"></div><div class="status"><div class="copyright">&copy; 2018 – <span itemprop="copyrightYear">2024</span> <span class="with-love"><i class="ic i-sakura rotate"></i> </span><span class="author" itemprop="copyrightHolder">Hualin Liu @ ResearchGo</span></div><div class="count"><span class="post-meta-item-icon"><i class="ic i-chart-area"></i> </span><span title="Symbols count total">788k words</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-coffee"></i> </span><span title="Reading time total">11:57</span><script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-info-circle"></i> </span><span style="padding-left:1px"><span id="busuanzi_container_site_pv">Total visits: <span id="busuanzi_value_site_pv" style="color:#f99"></span> times</span> <span class="post-meta-divider">|</span> <span class="post-meta-item-icon"><i class="ic i-user"></i> </span><span style="padding-left:1px"><span id="busuanzi_container_site_uv">Total visitors: <span id="busuanzi_value_site_uv" style="color:#f99"></span> people</span></span></span></div><div class="powered-by">Powered by <span class="exturl" data-url="aHR0cHM6Ly9oZXhvLmlv">Hexo</span> & Theme.<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2FtZWhpbWUvaGV4by10aGVtZS1zaG9rYQ==">Shoka</span></div></div></div></footer></div><script data-config type="text/javascript">var LOCAL={path:"post/e6be.html",favicon:{show:"（●´3｀●）Goooood",hide:"(´Д｀)Booooom"},search:{placeholder:"Search for Posts",empty:"We didn't find any results for the search: ${query}",stats:"${hits} results found in ${time} ms"},valine:!0,fancybox:!0,copyright:'Copied to clipboard successfully! <br> All articles in this blog are licensed under <i class="ic i-creative-commons"></i>BY-NC-SA.',ignores:[function(e){return e.includes("#")},function(e){return new RegExp(LOCAL.path+"$").test(e)}]}</script><script src="https://cdn.polyfill.io/v2/polyfill.js"></script><script src="//cdn.jsdelivr.net/combine/npm/pace-js@1.0.2/pace.min.js,npm/pjax@0.2.8/pjax.min.js,npm/whatwg-fetch@3.4.0/dist/fetch.umd.min.js,npm/animejs@3.2.0/lib/anime.min.js,npm/algoliasearch@4/dist/algoliasearch-lite.umd.js,npm/instantsearch.js@4/dist/instantsearch.production.min.js,npm/lozad@1/dist/lozad.min.js,npm/quicklink@2/dist/quicklink.umd.js"></script><script src="/js/app.js?v=0.2.5"></script><script data-pjax>var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="https://hm.baidu.com/hm.js?d211830a796ceefb38a141fde14e4d5a";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script></body></html>